package com.smart.service.oss;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.smart.common.entities.constant.SmartConstant;
import com.smart.common.entities.file.UploadBody;
import com.smart.common.entities.file.UploadResult;
import com.smart.common.exception.SmartException;
import com.smart.manager.system.repository.ConfigRepository;
import com.smart.utils.ContentTypeUtil;
import com.smart.utils.FileUtils;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Set;

/**
 * minio文件处理程序
 *
 * @author Administrator
 * @date 2022/09/09
 */
@Component  @Log4j2
@RequiredArgsConstructor
public class MinioFileHandler {

    private final ConfigRepository configRepository;

    private JSONObject readConfig() {
        JSONObject jsonObject = configRepository.readCache(SmartConstant.MINIO_OSS_CFG);
        Set<String> mustKeys = Set.of("accessKey", "secretKey", "bucket", "readPath");
        Assert.isTrue(jsonObject.keySet().containsAll(mustKeys), "Minio参数配置必须包含以下参数字段:" + mustKeys);
        Assert.isTrue(ObjectUtil.isAllNotEmpty(jsonObject), "Minio 参数配置存在空配置字段");
        return configRepository.readCache(SmartConstant.MINIO_OSS_CFG);

    }


    /**
     * 得到Minio客户端实体类
     *
     * @param config minio配置
     * @return {@link MinioClient}
     */
    private MinioClient getClient(JSONObject config) {
        return MinioClient.builder().endpoint(config.getString("readPath"))
                .credentials(config.getString("accessKey"), config.getString("secretKey"))
                .build();
    }

    private void checkBucket(MinioClient minioClient, String bucket) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
            }
        } catch (Exception e) {
            throw new SmartException("创建Minio存储桶失败:" + e.getMessage());
        }
    }

    /**
     * 得到上传结果
     *
     * @param newFileName 新文件名字
     * @param config      配置
     * @return {@link UploadResult}
     */
    private UploadResult getUploadResult(String newFileName, JSONObject config) {
        String affectPath = config.getString("readPath").concat("/").concat( config.getString("bucket")).concat("/").concat(newFileName);
        return new UploadResult().setUid(StrUtil.uuid()).setName(newFileName).setUrl(affectPath);
    }

    /**
     * 得到上传结果
     * @param newFileName 新文件名字
     * @param config      配置
     * @param fileName    原文件名称
     * @return {@link UploadResult}
     */
    private UploadResult getUploadResult(String newFileName, JSONObject config,String fileName) {
        String affectPath = config.getString("readPath").concat("/").concat( config.getString("bucket")).concat("/").concat(newFileName);
        return new UploadResult().setUid(StrUtil.uuid()).setName(fileName).setUrl(affectPath);
    }



    /**
     * Minio上传图片
     */
    public UploadResult uploadImg(MultipartFile file, String topFolder, String fileNamePrefix) {
        JSONObject config = this.readConfig();
        MinioClient minioClient = this.getClient(config);
        String filename = file.getOriginalFilename();
        Assert.notBlank(filename, "文件名称读取失败,请检查上传的文件格式是否正确");
        String ext = filename.substring(filename.indexOf("."));
        String newFileName = FileUtils.getFileName(topFolder, ext, fileNamePrefix);
        try {

            String bucket= config.getString("bucket");
            this.checkBucket(minioClient, bucket);
            PutObjectArgs objectArgs = PutObjectArgs.builder().object(newFileName).bucket(bucket)
                    .contentType(file.getContentType()).stream(file.getInputStream(), file.getSize(), -1).build();
            minioClient.putObject(objectArgs);
            return getUploadResult(newFileName, config);
        } catch (Exception e) {
            throw new SmartException("文件上传失败:错误原因" + e.getMessage());

        }

    }


    /**
     * Minio上传文件
     */
    public UploadResult uploadFile(MultipartFile file, String topFolder, String fileNamePrefix) {
        JSONObject config = this.readConfig();
        MinioClient minioClient = this.getClient(config);
        String filename = file.getOriginalFilename();
        Assert.notBlank(filename, "文件名称读取失败,请检查上传的文件格式是否正确");
        String ext = filename.substring(filename.indexOf("."));
        String newFileName = FileUtils.getFileName(topFolder, ext, fileNamePrefix);
        try {
            String bucket= config.getString("bucket");
            this.checkBucket(minioClient, bucket);

            PutObjectArgs objectArgs = PutObjectArgs.builder().object(newFileName).bucket(bucket)
                    .contentType(file.getContentType()).stream(file.getInputStream(), file.getSize(), -1).build();
            minioClient.putObject(objectArgs);
            return getUploadResult(newFileName, config,filename);
        } catch (Exception e) {

            throw new SmartException("文件上传失败:错误原因" + e.getMessage());
        }

    }


    /**
     * Minio Base64上传文件
     */
    public UploadResult uploadFile(UploadBody.Base64 dto, String topFolder, String fileNamePrefix) {
        JSONObject config = this.readConfig();

        MinioClient minioClient = this.getClient(config);

        String bucket= config.getString("bucket");
        this.checkBucket(minioClient, bucket);

        //解析BASE64字符串内的文件类型
        String contentType = dto.getBase64().substring(0, dto.getBase64().indexOf(",")).replace("data:", "").replace(";base64", "");
        String ext = ContentTypeUtil.getExt(contentType);
        byte[] bytes = FileUtils.base64StringToByte(dto.getBase64());
        InputStream inputStream = new ByteArrayInputStream(bytes);
        Assert.notNull(ext, "文件格式识别失败 请检查BASE64文件格式是否正确");

        String newFileName = FileUtils.getFileName(topFolder, ext, fileNamePrefix);


        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(newFileName)
                    .bucket(bucket)
                    .contentType(contentType)
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            return getUploadResult(newFileName, config);

        } catch (Exception e) {
            throw new SmartException("文件上传失败:错误原因" + e.getMessage());
        }

    }


    /**
     * Minio Byte上传文件
     */
    public UploadResult uploadFile(UploadBody.ByteFile dto, String topFolder, String fileNamePrefix) {
        JSONObject config = this.readConfig();
        MinioClient minioClient = this.getClient(config);
        String bucket= config.getString("bucket");
        String contentType = dto.getContentType();
        //解析BASE64字符串内的文件类型
        String s = ContentTypeUtil.getExt(contentType);
        String ext = StrUtil.isBlank(s) ? ".xlsx" : s;
        InputStream inputStream = new ByteArrayInputStream(dto.getData());
        String newFileName = FileUtils.getFileName(topFolder, ext, fileNamePrefix);
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().object(newFileName)
                    .bucket(bucket).contentType(contentType)
                    .stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(objectArgs);

            return getUploadResult(newFileName, config);
        } catch (Exception e) {
            e.printStackTrace();
            throw new SmartException("文件上传失败:错误原因" + e.getMessage());
        }


    }


}
